/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package helpdiabetesj2me;

import java.util.Timer;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

/**
 *
 * @author Johan
 */
public abstract class MyCanvas extends Canvas {
    
    int y = 0;int x = 0; // will be used to keep position where next char, .. can be drawn
    
    // the font to be used in graphics
    Font userfont;
    
    static int scrwidth;//screen width
    static int scrhght;//will be the screen height
    
    // the height of the font being used
    int fonthght;

    //constant values for key_codes, to be used by derived classes to check on keypresses
    static final int Key_code_Change_size=5010;
    static final int Key_code_Close=5011;
    static final int Key_code_UP=5012;
    static final int Key_code_DOWN=5013;
    static final int Key_code_LEFT=5014;
    static final int Key_code_RIGHT=5015;
    static final int Key_code_FIRE=5016;    

    static final int[][] keycodes = {
        {KEY_NUM1,KEY_NUM2,KEY_NUM3,Key_code_Change_size},
        {KEY_NUM4,KEY_NUM5,KEY_NUM6,Key_code_UP},
        {KEY_NUM7,KEY_NUM8,KEY_NUM9,Key_code_DOWN},
        {Key_code_LEFT,KEY_NUM0,Key_code_RIGHT,Key_code_Close}

        };

    // cursor ise a short vertical blinking line, as a high as the font size,
    // this variable will be switched from true to false to true by flipfoptimertask
    // then paint will use the value to determine of cursor needs to be shown or not
    protected boolean ShowCursor;

    // timer for making the cursor blink
    static private Timer flipflopTimer;
	
    // this object will be used to keep the characters entered by the user
    // and to synchronize communication between methods 'SearchFoodItem' and the methods that handle the key presses (keyConfirmed, keyPressed, ..).
    SearchString enteredstring;
    
    //used in several methods to retrieve record
    //protected byte[] oldb;
    static protected byte[] b;
    public static final int maxblength = 500;
    static int blength;
    
    //my personal extensions to possible keyCodes for Sony Ericsson. Used in keyPressed to check if back or clear are used.
    static final int BACK_KEY = -11;
    static final int CLEAR_KEY = -8;
    
    //will be the midlet
    static HelpDiabetes midlet;
    
    //can be used to store the current displayable
    Displayable curdisp;
    
    //used in paintstring
    static int linewidth;
    static int temp1;
    static char ch;
    
    // will be used to exit the application
    static Timer exittimer;
    
    // this array defines the sortorder to be used
        // example : the user enters character 's', ascii value of 's' = 115
        // charorder[115] = 224. Excel puts the 's' on place 221 but I'm treating 'S', 's', 'Š', 'š' and 'ß' as the same order.
        // Which means these variants of 's' are considered as equal. 224 is the place where Excel buts 'ß'.
        static final char[] charorder = {  0,   1,  2,  3,  4,  5,  6,  7,  8,  40,
                                           41,  42, 43, 44, 9,  10, 11, 12, 13, 14,
                                           15,  16, 17, 18, 19, 20, 21, 22, 23, 24,
                                           25,  26, 38, 45, 46, 47, 48, 49, 50, 33,
                                           51,  52, 53, 88,  0, 34, 55, 56,115,120,//44 which is ascii code for , is being mapped to 0, this is because , will never be shown in list, should not appear in text, and only appears in byte range as delimiter between the different fields
                                           122,124,125,126,127,128,129,130, 57, 58,
                                           89,  90, 91, 59, 60,147,149,153,157,167,
                                           170,172,174,184,186,188,190,192,196,213,
                                           215,217,219,224,229,239,241,243,245,251,
                                           255, 61, 62, 63, 64, 66, 67,147,149,153,
                                           157,167,170,172,174,184,186,188,190,192,
                                           196,213,215,217,219,224,229,239,241,243,
                                           245,251,255, 68, 69, 70, 71, 27,114, 28,
                                           82 ,170, 85,112,109,110, 65,113,224, 86,
                                           213, 29,255, 30, 31, 80, 81, 83, 84,111,
                                           36,  37, 79,229,224, 87,213, 32,255,251,
                                           39,  72, 97, 98, 99,100, 73,101, 74,102,
                                           147, 93,103, 35,104, 75,105, 92,122,124,
                                           76, 106,107,108, 77,120,213, 94,116,117,
                                           118, 78,147,147,147,147,147,147,147,153,
                                           167,167,167,167,184,184,184,184,157,196,
                                           213,213,213,213,213, 95,213,239,239,239,
                                           239,251,229,224,147,147,147,147,147,147,
                                           147,153,167,167,167,167,184,184,184,184,
                                           157,196,213,213,213,213,213, 96,213,239,
                                           239,239,239,251,229,251};
    private boolean scrollbarhit=false;

    //moved from Additem to here
    int unit; // will be the int value of the unit chosen
    //moved from Additem to here
    public static  String[] units = {//defines the units used, foodfile contains in the second field an integer pointing to the unit. 0 is not used
        // the list of units is based on the units used in the file Koolhydraten.pdf as can be found on www.pancreas.nl
        // some units will be recalculated to standard units, eg if the unit in the list is "100 gr", the actual unit displayed
        // on the screen will be "gram".
        //*********************
        // modification done on 25/02
        // makes actually no sense to first input integer values (the csv file) and then replace them by strings
        // I changed my mind. To avoid high impact and to make sure the program still works for users that already use the .csv file and/or
        // that have already the application installed using Integers, this is what i did :
        // Whenver parsing of the unit string to Integer fails, it means the input does contain a string, not an Integer
        // then I use unit[8] to store the value of this string (unit[8] wasn't used yet). This can be seen in method ReadItemList
        // and in the class GetItemfromUser, when method ItemSelected is called'
        //*********************
    "Not used"   , // the foodfile will never contain 0 as unit
    "", // = 1 = the unit used is 100 gram, which means the number of kcal, eiwit, koolhydraten and vet correspond to 100 gram of the stuff
    "", // = 2 = een blikje zoals het standaard voor dit soort produkt wordt gebruikt, vb blikje cola, blikje aquarius
    "", // = 3 =
    "", // = 4 =
    "", // = 5 =
    "", // = 6 =
    "", // = 7 =
    "none",// - will be used to store string values - see comments above
    "", // = 9 =
    "", // = 10 =
    "", // = 11 =
    "stuks", // = 12 =
    "teentje", // = 13 =
    "", // = 14 =
    "10 cl", // = 15 = 10 centileter, will never be displayed, amount will be recalculated to milliliter
    "10 gram", // = 16 = 10 gram, will never be displayed, amount will be recalculated to gram
    "100 gram", // = 17 = 100 gram, will never be displayed, amount will be recalculated to gram
    "100 ml", // = 18 = 100 ml, will never be displayed, amount will be recalculated to milliliter
    "25 cl", // = 19 = 25 cl, will never be displayed, amount will be recalculated to milliliter
    "35 gram", // = 20 = 35 gram, will never be displayed, amount will be recalculated to gram
    "", // = 21 = the unit used is 100 milliliter, which means the number of kcal, eiwit, koolhydraten and vet correspond to 100 milliliter of the stuff
    "fl oz", // = 22 =
    "oz-wt" // = 23 =
    };
    int std_amnt;
    Float eiwit;
    Float kcal;
    Float koolh;
    Float vet;
    //added 23/06 to support christophe jacobs requirement : to give weight of one unit
    Float weight_of_one_unit;

    /** Creates a new instance of MyCanvas */
    // it's a bit stupid but when creating an instance of this class and after setting display to instance of this class, setscrsize() needs to be called
    // '
    public MyCanvas(Font userfont,HelpDiabetes midlet)  {
        this.userfont = userfont;
        this.midlet = midlet;
        fonthght = userfont.getHeight();
        ShowCursor = false;// false means not displayed currently, it will flip flop constantly except when were waiting for a repetition of a keypress
        
        enteredstring = new SearchString();

        resetexittimer(this);
        
        if (b == null) {
            b = new byte[maxblength] ;
            blength = 0;
        }
                
        setFullScreenMode(false);

    }
    

    void flipflopthecursor() {
        if (ShowCursor) ShowCursor = false;
        else ShowCursor = true;

        //in case of flipflop now need to repaint the whole screen in getitemfromuser.paint()
        repaint();
        resetflipfloptimer(this);
    }

    
    // a version of paintstring without length of the byte array and wrap specified. Length of toprint will be used
    boolean paintstring(byte[] toprint,boolean invert,Graphics graphics,Font thefont){
        return paintstring(toprint,toprint.length,invert,graphics,thefont,false);
    }

    // a version of paintstring without wrap specified
    boolean paintstring(byte[] toprint,int length, boolean invert,Graphics graphics,Font thefont){
        return paintstring(toprint,length,invert,graphics,thefont,false);
    }

    // a version of paintstring without length but with wrap specified
    boolean paintstring(byte[] toprint, boolean invert,Graphics graphics,Font thefont, boolean wrap){
        return paintstring(toprint,toprint.length,invert,graphics,thefont,wrap);
    }

    
    // Will be used in paint function to print strings 
    // Position is determined by current values of x and y which are global variables in class getitemfromuser
    // This method will set x and y to new values pointing to the next position where new string can be drawn
    // Will do automatic carriage return when screenwidth is reached
    // Assumption that screen is already set to white before calling this function
    // toprint is the string to print in the form of a byte array
    // invert true means background black and letters white / false means background white and letters black
    // Grahpics color at the end of this method undefined.
    // thefont is the Font to be used
    // length is the actual length of the array to be considered
    // wrap true means the method will try to put whole words on the next line, this is not the case for the inverted part
    // return true means the string was completely printed on the screen
    // return false means the string was not completely printed on the screen
    boolean paintstring(byte[] toprint,int length,boolean invert,Graphics graphics,Font thefont, boolean wrap){
        int neededlength;
        int temp2;

        if ((length == 0) | (scrwidth == 0)) //this may happen because paint is called before scrwidth and zoekb get initalised.
            {return true;}
        graphics.setFont(thefont);
        // if invert = true then first calculate the total length of the string when printed and
        //    set the background to black for the part where the text will arrive
        
        // start by setting color to black, if invert, then color will be set to white during check "if (invert)"
        graphics.setColor(0,0,0);
        
        if (invert) {
            // drawing first the black part, just as long as the text is, taking into account 'carriage return'
            int x1 = x;
            int y1 = y;
            linewidth = x1;
            for (temp1 = 0;temp1 < length;temp1++) {
                ch = (char)(0xFF & toprint[temp1]);
                if (ch == '|') {ch = ',';}
                linewidth = linewidth + thefont.charWidth(ch);
                if ((char)(0xFF & (int)toprint[temp1]) == ',') {
                    break;//if the character is a ; then we stop
                } else if (linewidth > scrwidth) {
                    graphics.fillRect(x1,y1,linewidth,fonthght);
                    linewidth = thefont.charWidth(ch);
                    x1=0;y1=y1+fonthght;
                }
                
            }
            graphics.fillRect(x1,y1,linewidth,fonthght);
            // if invert then we'll print the strings in white'
            graphics.setColor(255,255,255);
        }
        
        linewidth = x;//we start drawing at position x
        for(temp1=0; temp1 < length; temp1++) {//using int x1 here
            if (!invert && wrap && ((char)(0xFF & (int)toprint[temp1]) == ' ') && (x > 0)) {
                //we should try to put whole words on the next line
                //and the next character is a space, so a new word starts
                //and we're not at the lift side of the screen
                temp2 = 1;
                neededlength = thefont.charWidth(' ');
                while ( (temp1+temp2 < length) && ((char)(0xFF & (int)toprint[temp1+temp2]) != ' ') && ((char)(0xFF & (int)toprint[temp1+temp2]) != ',') ) {
                    neededlength = neededlength + thefont.charWidth((char)(0xFF & (int)toprint[temp1+temp2]));
                    temp2=temp2+1;
                }
                if ( (x + neededlength) > scrwidth) {
                    //word doesn't fit on the screen - go to next line
                    temp1=temp1+1;//skip the next space
                    x = 0;
                    linewidth=0;
                    y = y + fonthght;
                }
            }

            if ((char)(0xFF & (int)toprint[temp1]) == ',') {
                break;//if the character is a ; then we stop
            } else {
                ch = (char)(0xFF & toprint[temp1]);
                if (ch == '|') {ch = ',';} //this is because the excel macro replace ',' by '|'
                //measure the char to draw
                linewidth = linewidth + thefont.charWidth(ch);
                //see if a new line is needed
                if (linewidth > scrwidth )
                {
                        y = y + fonthght;
                        linewidth = thefont.charWidth(ch);
                        x = 0;
                }
                //draw the char
                graphics.drawChar(ch, x, y,Graphics.TOP|Graphics.LEFT);
                x = linewidth;
            }
        } 
        if (y + fonthght < scrhght) {
            return true;
        } else {
            return false;
        }
    }

    // method to set the screen height and width 
    // to avoid having to call getwidt and getheight each time
    void setscrsize() {
        while (midlet.getDisplay().getCurrent() == null ) {
            try {
                Thread.sleep(400);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
         scrhght = midlet.getDisplay().getCurrent().getHeight();
         scrwidth = midlet.getDisplay().getCurrent().getWidth();
    }
    
    // getfirstsemicolon and getnextsemicolon are used together 
    // they return the index in b (attributes in MyCanvas class) of respectively the first and the next semicolon
    // When getfirstsemicolon is called, attribute temp1 will be set to the index, next time getnextsemicolon is called, 
    //        searching will start as of temp1+1
    // if not found or if b = null, returnvalue is -1
    // semicolon to be understood as ','
    int getfirstsemicolon () {
        int i;
        temp1 = -1;
        try {
            for (i = 0;i<blength;i++) {
                if ( ((char)(0xFF & b[i])) == ',') {
                    temp1 = i;
                    break;
                }
            }
        } catch (Exception e) {
            
        } finally {
            return temp1;
        }
    }
    
    //this method will only behave correctly if getfirstsemicolon has been called before and if temp1 has not been modified
    int getnextsemicolon () {
        int i;
        i = temp1+1;
        temp1=-1;
        try {
            for (;i<blength;i++) {
                if ( ((char)(0xFF & b[i])) == ',') {
                    temp1 = i;
                    break;
                }
            }
        } catch (Exception e) {
            
        } finally {
            return temp1;
        }
    }
    
    //prints the cursor on the current position x and y 
    void printcursor(Graphics graphics) {
        // draw the cursor, if necessary.
        // if showcursor = false, no need to do anything because it's already erased by erasing the wholescreen earlier on'
        if (ShowCursor) {
           graphics.drawLine(x+1,y,x+1,y + fonthght);
        }
        
    }
    
    
    
    void resetexittimer(MyCanvas mycanvas) {
        if (exittimer != null) {
            exittimer.cancel();
            exittimer = null;
        }
        exittimer = new Timer();
        exittimer.schedule(new ExitTimer(mycanvas),1200000);
    }

    protected void showNotify() {
        resetexittimer(this);
        resetflipfloptimer(this);
    }
    
    // sets x to 0 and y to y + local fonthght + inc;
    // this is like entering a newline and adding some space if wanted
    void newline(int inc) {
        x = 0;
        y = y + fonthght + inc;
    }

    //will reset the flipfloptimer
    //mycanvas : is the displayable for which the timer will be running. The timer itself is a static variable, 
    //so we must make sure that the reset occurs for the current displayable
    public void resetflipfloptimer(Displayable mycanvas) {
        cancelflipfloptimer();
        flipflopTimer = new Timer();
        flipflopTimer.schedule(new FlipFlopCursor(mycanvas),500);
    }

    public void cancelflipfloptimer() {
        if (flipflopTimer != null) {
            flipflopTimer.cancel();
            flipflopTimer = null;
        }
    }

      //method to paint keyboard on screen

    

      //this is a name that will be used here to use hdproperties.
      //name will be used as key to store the type of keyboard in the properties
      abstract String GetClassName() ;
      
      //it's necessary to overload getGameAction because the Canvas.getGameAction throws a runtime exception if keyCode has value in 5000 range
      public int getGameAction(int keyCode) {
      	switch (keyCode) {
      		case Key_code_UP: 
      		   return UP;
      		case Key_code_DOWN: 
      		   return DOWN;
      		case Key_code_LEFT: 
      		   return LEFT;
      		case Key_code_RIGHT: 
      		   return RIGHT;
      		case Key_code_FIRE: 
      		   return FIRE;
      		default:
      			 return super.getGameAction(keyCode);
        }	
      }

   // b must be initialized to a byte array containing a record from foodrs
   // this method will calculate kcal, eiwit, vet, koolh based in the value of b
   // kcal, eiwit, vet, koolh will respectively be the amounts of kcal, eiwit, vet, koolh per unit
   // unitnumber is added on 2/11/2008 to support multiple units per fooditem
   //moved this method from additem to here on 16/06
   // 30/03/2010, changed return type to int, returnvalue will be std-amount, will be used in UnitList.CommandAction
   public int CalculateAmounts_in_b(int unitnumber) {
       int i1,i2=0;
       weight_of_one_unit= new Float(1);//if nothing specified in unit field then 1

            // read all the values into floats and one int
            i1=getfirstsemicolon();
            //we need to skip a few columns in the food record store (with columns referring to the columns used in excel, depending on the value of unitnumber
            for (int itemp = 1;itemp < unitnumber;itemp++) {

                i2=getnextsemicolon();i2=getnextsemicolon();i2=getnextsemicolon();i2=getnextsemicolon();i2=getnextsemicolon();i2=getnextsemicolon();
                i1=i2;
            }

            i2 = getnextsemicolon();
            units[8] = new String(b,i1+1,i2-i1-1);
            try {
                unit = Integer.parseInt(units[8]);
            } catch (NumberFormatException ex) {
                unit = 8;//means unit is a string, not an integer
            }
            //added 23/06 : check if unit contains a "=", if yes split and use interger part as weight_of_one_unit
            if (units[8].indexOf("=") > -1 ) {
            		weight_of_one_unit = Float.parse(units[8].substring(units[8].indexOf("=")+1),10);
            }	


            i1 = i2;
            i2 = getnextsemicolon();
            if (i2 > i1 + 1) {
                //there's a standard amount filled in, this should be an integer
                std_amnt = Integer.parseInt(new String(b,i1+1,i2-i1-1));
            } else {
                //standard amount field is empty, we'll use '1'
                std_amnt = 1;
            }
            i1 = i2;
            i2 = getnextsemicolon();
            if (i2 > i1 + 1) {
                kcal = Float.parse(new String(b,i1+1,i2-i1-1),10);
            } else {
                kcal = new Float(Float.ERROR);
            }
            i1 = i2;
            i2 = getnextsemicolon();
            if (i2 > i1 + 1) {
                eiwit = Float.parse(new String(b,i1+1,i2-i1-1),10);
            } else {
                eiwit = new Float(Float.ERROR);
            }
            i1 = i2;
            i2 = getnextsemicolon();
            if (i2 > i1 + 1) {
                koolh = Float.parse(new String(b,i1+1,i2-i1-1),10);
            } else {
                koolh = new Float(Float.ERROR);
            }
            i1 = i2;
            i2 = getnextsemicolon();
            if (i2 < 0)
                i2 = blength;
            if (i2 > i1 + 1) {
                vet = Float.parse(new String(b,i1+1,i2-i1-1),10);
            } else {
                vet = new Float(Float.ERROR);
            }
            kcal = kcal.Div(std_amnt);
            eiwit = eiwit.Div(std_amnt);
            koolh = koolh.Div(std_amnt);
            vet = vet.Div(std_amnt);

            //added on 30/03/2010
            return std_amnt;
   }

}

